package com.itxixi.domain.strategy.service.raffle;


import com.itxixi.domain.strategy.model.entity.RaffleAwardEntity;
import com.itxixi.domain.strategy.model.entity.RaffleFactorEntity;
import com.itxixi.domain.strategy.model.entity.RuleActionEntity;
import com.itxixi.domain.strategy.model.entity.StrategyEntity;
import com.itxixi.domain.strategy.model.valobj.RuleLogicCheckTypeVO;
import com.itxixi.domain.strategy.repository.IStrategyRepository;
import com.itxixi.domain.strategy.service.IRaffleStrategy;
import com.itxixi.domain.strategy.service.armory.IStrategyDispatch;
import com.itxixi.domain.strategy.service.rule.factory.DefaultLogicFactory;
import com.itxixi.types.enums.ResponseCode;
import com.itxixi.types.exception.AppException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

/**
 * @author Fuzhengwei bugstack.cn @小傅哥
 * @description 抽奖策略抽象类，定义抽奖的标准流程  定义模板提供抽象方法
 *        对应模板设计模式        实现接口里的方法  对这个方法做一些标准性的定义
 *                                     把一些通用的东西 和 流程的东西 都由这里边提供出来
 * @create 2024-01-06 09:26
 */
@Slf4j
public abstract class AbstractRaffleStrategy implements IRaffleStrategy {

    // 策略仓储服务 -> domain层像一个大厨，仓储层提供米面粮油
    protected IStrategyRepository repository;
    // 策略调度服务 -> 只负责抽奖处理，通过新增接口的方式，隔离职责，不需要使用方关心或者调用抽奖的初始化
    protected IStrategyDispatch strategyDispatch;

    //通过构造函数注入 Spring能够在实例化Bean时提供所需的依赖，从而确保依赖关系的清晰和解耦
    //在创建AbstractRaffleStrategy 的实例时，Spring会识别出需要的依赖,Spring 会查找这些依赖的Bean定义，并确定如何注入它们。
    //流程：扫描bean与构造函数 解析依赖  实例化依赖bean 调用构造函数传入实例化的bean 得到AbstractRaffleStrategy实例 注入IOC
    public AbstractRaffleStrategy(IStrategyRepository repository, IStrategyDispatch strategyDispatch) {
        this.repository = repository;
        this.strategyDispatch = strategyDispatch;
    }

    @Override  //模板模式的模版方法  ： 定义抽奖流程
              // +  基本方法（流程） + 抽象方法（流程）  +  钩子方法
    public RaffleAwardEntity performRaffle(RaffleFactorEntity raffleFactorEntity) {
        // 1. 参数校验
        String userId = raffleFactorEntity.getUserId();
        Long strategyId = raffleFactorEntity.getStrategyId();
        if (null == strategyId || StringUtils.isBlank(userId)) {
            throw new AppException(ResponseCode.ILLEGAL_PARAMETER.getCode(), ResponseCode.ILLEGAL_PARAMETER.getInfo());
        }//这个方法被其他地方调用，那么调用这个方法的地方可以通过 try-catch 语句捕获

        // 2. 策略查询  就是查抽奖前的规则 这个Strategy表就是为了查对应的前置规则的 且表中只有前置规则 = rulemodel字段
                    ////到IStrategyDao中根据strategyId100001查找Strategy实体  方便得到rulemodel字段  //rule_blacklist,rule_weight
        StrategyEntity strategy = repository.queryStrategyEntityByStrategyId(strategyId);

        // 3. 抽奖前 - 规则过滤  返回类型也是抽奖前置的规则动作
        RuleActionEntity<RuleActionEntity.RaffleBeforeEntity> ruleActionEntity =                                    //rule_blacklist,rule_weight
                this.doCheckRaffleBeforeLogic(RaffleFactorEntity.builder().userId(userId).strategyId(strategyId).build(), strategy.ruleModels());
        //如果被接管 == 抽奖前置规则存在 RuleActionEntity ：code info ruleModel data(strategyId awardId ruleWeightValueKey）
        if (RuleLogicCheckTypeVO.TAKE_OVER.getCode().equals(ruleActionEntity.getCode())) {
                //如果是黑名单 直接返回 不用抽奖
            if (DefaultLogicFactory.LogicModel.RULE_BLACKLIST.getCode().equals(ruleActionEntity.getRuleModel())) {
                // 黑名单返回固定的奖品ID  直接通过查询到的ruleActionEntity返回awardId
                //.data(RuleActionEntity.RaffleBeforeEntity.builder()
                //               .strategyId(strategyId)//100001 黑名单strategyId没用
                //               .awardId(awardId)//100 意思黑名单直接去Award表里 返回awardId对应的返回awardConfig 1积分
                //                                .build()) //data要放入一个T RuleActionEntity子类对象
                return RaffleAwardEntity.builder()
                        .awardId(ruleActionEntity.getData().getAwardId())
                        .build();

                //如果是权重 则还要去抽奖
            } else if (DefaultLogicFactory.LogicModel.RULE_WIGHT.getCode().equals(ruleActionEntity.getRuleModel())) {
                // 权重根据返回的信息进行抽奖  .data(RuleActionEntity.RaffleBeforeEntity.builder()
                //                            .strategyId(strategyId)//100001
                //                            .ruleWeightValueKey(analyticalValueGroup.get(nextValue))//4000：102,103,104,105（String）
                //                            .build())
                RuleActionEntity.RaffleBeforeEntity raffleBeforeEntity = ruleActionEntity.getData();//获取RuleActionEntity.RaffleBeforeEntity实例
                String ruleWeightValueKey = raffleBeforeEntity.getRuleWeightValueKey();
                Integer awardId = strategyDispatch.getRandomAwardId(strategyId, ruleWeightValueKey);
                                                        // Long strategyId = raffleFactorEntity.getStrategyId();
                return RaffleAwardEntity.builder()
                        .awardId(awardId)
                        .build();
            }
        }
          //RaffleAwardEntity : strategyId;awardId;awardKey;awardConfig;awardDesc;

        // 4. 默认抽奖流程  过滤结束
        Integer awardId = strategyDispatch.getRandomAwardId(strategyId);

        return RaffleAwardEntity.builder()
                .awardId(awardId)
                .build();
    }
    //抽象方法  给子类实现  这里只定义流程
    protected abstract RuleActionEntity<RuleActionEntity.RaffleBeforeEntity> doCheckRaffleBeforeLogic(RaffleFactorEntity raffleFactorEntity, String... logics);

}
